<?php

namespace wangdian\tool;

use wangdian\exception\InvalidFileSystemException;
use wangdian\exception\InvalidResponseException;
use wangdian\exception\InvalidConfigException;
use Exception;

class WDTTools
{
    /**
     * 数组转xml内容
     *
     * @param array $data
     * @return null|string
     */
    public static function arr2json($data)
    {
        $json = json_encode($data, JSON_UNESCAPED_UNICODE);
        return $json === '[]' ? '{}' : $json;
    }

    /**
     * 解析JSON内容到数组
     *
     * @param string $json
     * @return array
     * @throws InvalidResponseException
     */
    public static function json2arr($json)
    {
        $result = json_decode($json, true);
        if (empty($result)) {
            throw new InvalidResponseException('invalid response.', '0');
        }
        return $result;
    }

    /**
     * 以get访问模拟访问
     *
     * @param string $url   访问URL
     * @param array  $query GET数
     * @param array  $options
     * @return boolean|string
     * @throws InvalidConfigException
     * @throws InvalidFileSystemException
     */
    public static function get($url, $query = [], $options = [])
    {
        $options['query'] = $query;
        return self::doRequest('get', $url, $options);
    }

    /**
     * 以post访问模拟访问
     *
     * @param string $url  访问URL
     * @param array  $data POST数据
     * @param array  $options
     * @return boolean|string
     * @throws InvalidConfigException
     * @throws InvalidFileSystemException
     */
    public static function post($url, $data = [], $options = [])
    {
        $options['data'] = $data;
        return self::doRequest('post', $url, $options);
    }

    /**
     * CURL模拟网络请求
     *
     * @param string $method  请求方法
     * @param string $url     请求方法
     * @param array  $options 请求参数[headers,data,ssl_cer,ssl_key]
     * @return boolean|string
     * @throws InvalidConfigException
     * @throws InvalidFileSystemException
     */
    public static function doRequest($method, $url, $options = [])
    {
        $curl = curl_init();
        // GET参数设置
        if (!empty($options['query'])) {
            $url .= (stripos($url, '?') !== false ? '&' : '?') . http_build_query($options['query']);
        }
        // CURL头信息设置
        if (!empty($options['headers'])) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']);
        }
        // POST数据设置
        if (strtolower($method) === 'post') {
            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, self::buildHttpData($options['data']));
        }
        // 证书文件设置
        if (!empty($options['ssl_cer'])) {
            if (file_exists($options['ssl_cer'])) {
                curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM');
                curl_setopt($curl, CURLOPT_SSLCERT, $options['ssl_cer']);
            } else {
                throw new InvalidConfigException("Certificate files that do not exist. --- [ssl_cer]");
            }
        }
        // 证书文件设置
        if (!empty($options['ssl_key'])) {
            if (file_exists($options['ssl_key'])) {
                curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM');
                curl_setopt($curl, CURLOPT_SSLKEY, $options['ssl_key']);
            } else {
                throw new InvalidConfigException("Certificate files that do not exist. --- [ssl_key]");
            }
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_TIMEOUT, 60);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        list($content) = [curl_exec($curl), curl_close($curl)];
        self::log($url, json_encode($options), $content);
        return $content;
    }

    /**
     * POST数据过滤处理
     *
     * @param array   $data  需要处理的数据
     * @param boolean $build 是否编译数据
     * @return array|string
     */
    private static function buildHttpData($data, $build = true)
    {
        if (!is_array($data)) {
            return $data;
        }
        return $build ? http_build_query($data) : $data;
    }

    /**
     * 解析url中参数信息，返回参数数组
     *
     * @param $query
     * @return array
     */
    public static function convertUrlQuery($query)
    {
        $queryParts = explode('&', $query);

        $params = array();
        foreach ($queryParts as $param) {
            $item = explode('=', $param);
            $params[$item[0]] = $item[1];
        }

        return $params;
    }

    /**
     * 把数组拼接成url参数形式
     *
     * @param $array_query
     * @return string
     */
    public static function getUrlQuery($array_query)
    {
        $tmp = array();
        foreach ($array_query as $k => $param) {
            $tmp[] = $k . '=' . $param;
        }
        return implode('&', $tmp);
    }

    /**
     * @param $url
     * @return mixed|string
     */
    public static function removeQueryStringFromUrl($url)
    {
        if (substr($url, 0, 4) == "http") {
            $urlPartsArray = parse_url($url);
            $outputUrl = $urlPartsArray['scheme'] . '://' . $urlPartsArray['host'] .
                (isset($urlPartsArray['path']) ? $urlPartsArray['path'] : '');
        } else {
            $URLexploded = explode("?", $url, 2);
            $outputUrl = $URLexploded[0];
        }
        return $outputUrl;
    }

    /**
     * @param $data
     * @return string
     * @throws InvalidConfigException
     */
    public static function sign(&$data)
    {
        if (!isset($data['appsecret'])) {
            throw new InvalidConfigException('sign method appsecret is required');
        }
        $appSecret = $data['appsecret'];
        unset($data['appsecret']);
        ksort($data);
        $arr = array();
        foreach ($data as $key => $val) {
            if ($key == 'sign') {
                continue;
            }
            if (count($arr)) {
                $arr[] = ';';
            }
            $arr[] = sprintf("%02d", iconv_strlen($key, 'UTF-8'));
            $arr[] = '-';
            $arr[] = $key;
            $arr[] = ':';
            $arr[] = sprintf("%04d", iconv_strlen($val, 'UTF-8'));
            $arr[] = '-';
            $arr[] = $val;
        }
        return strtolower(md5(implode('', $arr) . $appSecret));
    }

    /**
     * @param $url
     * @param $content
     * @param $response
     * @throws InvalidFileSystemException
     */
    public static function log($url, $content, $response)
    {
        $request_time = date("d/m/Y:H:i:s");
        $line = "{$request_time} {$url} [{$content} {$response}] \r\n\r\n";
        $y = date("Y");
        $m = date("m");
        $d = date("d");

        $path = \Yii::$app->getRuntimePath() . "/wdt_request/{$y}/{$m}";
        $file = "{$path}/{$y}{$m}{$d}.log";
        static::createDirectory($path);
        file_put_contents($file, $line, FILE_APPEND);
    }

    /**
     * Creates a new directory.
     *
     * This method is similar to the PHP `mkdir()` function except that
     * it uses `chmod()` to set the permission of the created directory
     * in order to avoid the impact of the `umask` setting.
     *
     * @param string $path      path of the directory to be created.
     * @param int    $mode      the permission to be set for the created directory.
     * @param bool   $recursive whether to create parent directories if they do not exist.
     * @return bool whether the directory is created successfully
     * @throws InvalidFileSystemException if the directory could not be created (i.e. php error due to parallel changes)
     */
    public static function createDirectory($path, $mode = 0775, $recursive = true)
    {
        if (is_dir($path)) {
            return true;
        }
        $parentDir = dirname($path);
        if ($recursive && !is_dir($parentDir) && $parentDir !== $path) {
            static::createDirectory($parentDir, $mode, true);
        }
        try {
            if (!mkdir($path, $mode)) {
                return false;
            }
        } catch (Exception $e) {
            if (!is_dir($path)) {
                throw new InvalidFileSystemException("Failed to create directory \"$path\": " .
                    $e->getMessage(), $e->getCode(), $e);
            }
        }
        try {
            return chmod($path, $mode);
        } catch (Exception $e) {
            throw new InvalidFileSystemException("Failed to change permissions for directory \"$path\": " .
                $e->getMessage(), $e->getCode(), $e);
        }
    }
}
